package cn.qu.v1.platform.inject.factory;

import cn.qu.v1.platform.inject.BeanContext;
import cn.qu.v1.platform.inject.BeanKey;
import cn.qu.v1.platform.inject.MethodBeanDef;
import cn.qu.v1.platform.inject.annotation.Qualifier;
import com.google.common.base.Strings;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by zh on 17/1/10.
 */
public class PluginFactoryBean<T> implements FactoryBean<T> {

    private boolean singleton = true;

    private T obj;

    private String bucketName;
    private BeanKey beanKey;
    private MethodBeanDef methodBeanDef;
    private ClassLoader classLoader;

    public PluginFactoryBean(String bucketName, ClassLoader classLoader,BeanKey beanKey, MethodBeanDef methodBeanDef) {
        this.bucketName = bucketName;
        this.beanKey = beanKey;
        this.methodBeanDef = methodBeanDef;
        this.classLoader = classLoader;
    }

    protected Annotation getParameterAnnotations(Annotation[] paramAnnos, Class<Qualifier> checkAnno) {
        if (paramAnnos == null || checkAnno == null) {
            return null;
        }
        for (Annotation annos : paramAnnos) {
            if (annos.annotationType() == checkAnno) {
                return annos;
            }
        }
        return null;
    }

    private T createBean() throws Exception {
        T t = null;
        Method method = methodBeanDef.getMethod();
        int count = method.getParameterCount();
        if (count > 0) {
            Class<?>[] paramTypes = method.getParameterTypes();
            Annotation[][] paramAnnos = method.getParameterAnnotations();

            List<Object> params = new ArrayList<>();
            for (int i = 0; i < paramAnnos.length; i++) {
                Annotation[] annos = paramAnnos[i];
                Class<?> type = paramTypes[i];
                Qualifier qualifier = (Qualifier) getParameterAnnotations(annos, Qualifier.class);

                String beanName = qualifier == null ? null : qualifier.value();
                String pluginName = qualifier == null || Strings.isNullOrEmpty(qualifier.pluginName()) ? bucketName : qualifier.pluginName();

                BeanKey key = BeanKey.getKey(beanName, type);

                PluginFactoryBean bean = beanContext.findFactoryBean(pluginName, key);
                params.add(bean.getObject());
            }
            t = (T) method.invoke(methodBeanDef.getObj(), params.toArray());
        } else {
            t = (T) method.invoke(methodBeanDef.getObj());
        }
        if (this.isSingleton()) {
            obj = t;
        }
        return obj;
    }

    @Override
    public T getObject() throws Exception {
        if (obj == null) {
            return createBean();
        }
        return obj;
    }

    @Override
    public boolean isSingleton() {
        return singleton;
    }

    private BeanContext beanContext;

    public void setBeanContext(BeanContext beanContext) {
        this.beanContext = beanContext;
    }

    public BeanContext getBeanContext() {
        return beanContext;
    }
    public String getBucketName() {
        return bucketName;
    }

    public BeanKey getBeanKey() {
        return beanKey;
    }

    public MethodBeanDef getMethodBeanDef() {
        return methodBeanDef;
    }

    public ClassLoader getClassLoader() {
        return classLoader;
    }
}
